//------------------------------------------------------------------------------
// <copyright file="RpcSocketChannel.cpp" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------

#include "stdafx.h"

#include "RpcSocketChannel.h"
#include "RpcVersion.h"
#include "RpcSocketTransportMessage.h"

using namespace Microsoft::Internal::GamesTest::Rpc::Server;
using namespace std;

namespace
{
	const RpcSocketChannel::RequestReceivedCallback EmptyRequestReceivedCallback = [](std::weak_ptr<IRpcChannel>, RpcSocketTransportMessage) { };
	const RpcSocketChannel::ChannelDisconnectedCallback EmptyChannelDisconnectedCallback = [](std::weak_ptr<IRpcChannel>) { };
}

RpcSocketChannel::RpcSocketChannel(IRpcStringSocket* socket)
	: m_socket(socket), m_requestReceivedCallback(EmptyRequestReceivedCallback), m_channelDisconnectedCallback(EmptyChannelDisconnectedCallback)
{
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcSocketChannel::SendRpcMessage(const std::wstring& message, const GUID& serverId, HRESULT errorCode /* = S_OK */)
{
	HRESULT hr = E_FAIL;
	std::wstring serializedData;
	RpcSocketTransportMessage socketTransportMessage;

	socketTransportMessage.rpcMessage = message;
	socketTransportMessage.serverId = serverId;
	socketTransportMessage.errorCode = errorCode;
	socketTransportMessage.version = GAMESTEST_RPC_VERSION;

	CHK(socketTransportMessage.Serialize(serializedData));
	CHK(m_socket->SendData(serializedData));

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcSocketChannel::StartListening(RequestReceivedCallback requestReceived, ChannelDisconnectedCallback channelDisconnected)
{
	HRESULT hr = S_OK;
	m_requestReceivedCallback = requestReceived;
	m_channelDisconnectedCallback = channelDisconnected;

	IRpcStringSocket::DataReceivedCallback onStringReceived = std::bind(&RpcSocketChannel::OnStringReceived, this, std::placeholders::_1, std::placeholders::_2);
	IRpcStringSocket::SocketDisconnectedCallback onSocketDisconnected = std::bind(&RpcSocketChannel::OnSocketDisconnected, this, std::placeholders::_1);
	CHK(m_socket->StartListening(onStringReceived, onSocketDisconnected));
	
end:
	return hr;
}

void RpcSocketChannel::OnStringReceived(IRpcStringSocket* socket, std::wstring data)
{
	UNREFERENCED_PARAMETER(socket);

	HRESULT hr = E_FAIL;

	RpcSocketTransportMessage socketRequest;
	hr = RpcSocketTransportMessage::Parse(data, &socketRequest);

	if(FAILED(hr))
	{
		hr = GAMESTEST_RPC_E_FAILED_TO_PARSE_RPC_SOCKET_REQUEST;
		goto end;
	}

	m_requestReceivedCallback(this->shared_from_this(), socketRequest);

end:
	if(FAILED(hr))
	{
		RpcPrintMessage(L"Failed to handle incoming RpcSocketRequest:");
		RpcPrintMessage(data.c_str());
		RpcPrintError(hr);

		GUID unknownServerId;
		ZeroMemory(&unknownServerId, sizeof(unknownServerId));
		HRESULT hr2 = SendRpcMessage(L"", unknownServerId, hr);
		if(FAILED(hr2))
		{
			RpcPrintMessage(L"Failed to send response to the client with error code.");
			RpcPrintError(hr2);
		}
	}
}

void RpcSocketChannel::OnSocketDisconnected(IRpcStringSocket* socket)
{
	UNREFERENCED_PARAMETER(socket);

	m_channelDisconnectedCallback(this->shared_from_this());
}

RpcSocketChannel::~RpcSocketChannel()
{
	if(m_socket != nullptr)
	{
		delete m_socket;
	}
}